<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Custom widget in PHP GTK</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Custom widget, PHP, GTK, tutorial, programming, Linux">
<meta name="description" content="In this part of the PHP GTK tutorial, we create a custom widget.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>

<h1>Custom widget</h1>


<p>
Toolkits usually provide only the 
most common widgets like buttons, text widgets, sliders etc. No toolkit can provide all 
possible widgets. If there is a need for a more specialized widget, we must create it ourselves.
</p>

<div class="center">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* top_horizontal */
google_ad_slot = "3327173442";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p>
Custom widgets are created by using the drawing tools provided by the toolkit. 
There are two possibilities. A programmer can modify or enhance an existing widget. 
Or he can create a custom widget from scratch.
</p>

<h2>Burning widget</h2>

<p>
This is an example of a widget, that we create from scratch. It is based on a minimal
<code>GtkWidget</code> widget. This custom widget can be found in various media
burning applications, like Nero Burning ROM.
</p>

<pre class="code">
&lt;?php
 
/* 
ZetCode PHP GTK tutorial

This example creates a burning
custom widget.

author: Jan Bodnar
website: www.zetcode.com
last modified: August 2011
*/

class Burning extends GtkDrawingArea { 
     

    public function __construct($par) { 

        parent::__construct(); 
 
        $this-&gt;par = $par;          

        $this-&gt;init_ui();

    } 

    public function init_ui() {

        $this-&gt;num = array("75", "150", "225", "300", 
            "375", "450", "525", "600", "675");
 
        $this-&gt;set_size_request(1, 30);
        $this-&gt;connect('expose_event', array($this, 'on_expose')); 
 
    }

    public function on_expose() {
        $cr = $this-&gt;window-&gt;cairo_create();
        $this-&gt;draw_widget($cr);
    }

    public function draw_widget($cr) {
 
        $cr-&gt;SetLineWidth(0.8);
        $cr-&gt;SelectFontFace("Courier", CairoFontSlant::NORMAL, 
            CairoFontWeight::NORMAL);
        $cr-&gt;SetFontSize(11);

        $width = $this-&gt;get_allocation()-&gt;width;
     
        $this-&gt;cur_width = $this-&gt;par-&gt;get_cur_value();

        $step = round($width / 10.0);

        $till = ($width / 750.0) * $this-&gt;cur_width;
        $full = ($width / 750.0) * 700;

        if ($this-&gt;cur_width &gt;= 700) {
            
            $cr-&gt;SetSourceRgb(1.0, 1.0, 0.72);
            $cr-&gt;Rectangle(0, 0, $full, 30);
            $cr-&gt;Clip();
            $cr-&gt;Paint();
            $cr-&gt;ResetClip();
            
            $cr-&gt;SetSourceRgb(1.0, 0.68, 0.68).
            $cr-&gt;Rectangle($full, 0, $till-$full, 30);
            $cr-&gt;Clip();
            $cr-&gt;Paint();
            $cr-&gt;ResetClip();

        } else {
            $cr-&gt;SetSourceRgb(1.0, 1.0, 0.72);
            $cr-&gt;Rectangle(0, 0, $till, 30);
            $cr-&gt;Clip();
            $cr-&gt;Paint();
            $cr-&gt;ResetClip();
        }
       

        $cr-&gt;SetSourceRgb(0.35, 0.31, 0.24);
        $len = count($this-&gt;num);

        for ($i=1; $i &lt;= $len; $i++) {
            $cr-&gt;MoveTo($i*$step, 0);
            $cr-&gt;LineTo($i*$step, 5);
            $cr-&gt;Stroke();
            
            $te = $cr-&gt;TextExtents($this-&gt;num[$i-1]);
            $cr-&gt;MoveTo($i*$step-$te['width']/2, 15);
            $cr-&gt;TextPath($this-&gt;num[$i-1]);
            $cr-&gt;Stroke();
        }        
    }
}
            

class Example extends GtkWindow { 
     

    public function __construct() { 

        parent::__construct(); 
         
        $this-&gt;init_ui();

    } 

    private function init_ui() {

        $this-&gt;set_title('Burning widget');         
        $this-&gt;connect_simple('destroy', array('gtk', 'main_quit')); 

        $this-&gt;cur_value = 0;
       
        $vbox = new GtkVBox(false, 2);
        
        $scale = new GtkHScale();
        $scale-&gt;set_range(0, 750);
        $scale-&gt;set_digits(0);
        $scale-&gt;set_size_request(160, 35);
        $scale-&gt;set_value($this-&gt;cur_value);
        
        $scale-&gt;connect('value-changed', array($this, 'on_changed'));
        
        $fixed = new GtkFixed();
        $fixed-&gt;put($scale, 50, 50);
        
        $vbox-&gt;pack_start($fixed);
        
        $this-&gt;burning = new Burning($this);
        $vbox-&gt;pack_start($this-&gt;burning, false, false, 0);

        $this-&gt;add($vbox);

        $this-&gt;set_default_size(350, 200); 
        $this-&gt;set_position(GTK::WIN_POS_CENTER);
        $this-&gt;show_all();         
    }

    public function on_changed($sender) {
    
        $this-&gt;cur_value = $sender-&gt;get_value();
        $this-&gt;burning-&gt;queue_draw();
    }
    
    public function get_cur_value() {
        return $this-&gt;cur_value;
    }
} 
     
new Example(); 
Gtk::main();
 
?&gt;
</pre>

<p>
We put a <code>GtkDrawingArea</code> on the bottom of the window and draw the entire widget manually. 
All the important code resides in the <code>draw_widget()</code> which is called from the
<code>on_expose()</code> method of the Burning class. 
This widget shows graphically the total capacity of a medium and the free space available to us. 
The widget is controlled by a scale widget. The minimum value of our custom widget is 0, the maximum is 750. 
If we reach value 700, we began drawing in red color. This normally indicates overburning.  
</p>

<pre class="explanation">
$this-&gt;num = array("75", "150", "225", "300", 
    "375", "450", "525", "600", "675");
</pre>

<p>
These numbers are shown on the burning widget. They show the capacity of the medium.
</p>


<pre class="explanation">
$this-&gt;cur_width = $this-&gt;par-&gt;get_cur_value();
</pre>

<p>
From the parent widget, we get the current value of the scale widget.
</p>

<pre class="explanation">
$till = ($width / 750.0) * $this-&gt;cur_width;
$full = ($width / 750.0) * 700;
</pre>

<p>
We use the <code>$width</code> variable to do the transformations. Between 
the values of the scale and the custom widget's measures. Note that we use floating
point values. We get greater precision in drawing.
The <code>$till</code> parameter determines the total size to be drawn. This value comes 
from the slider widget. It is a proportion of the whole area. The <code>$full</code> 
parameter determines the point, where we begin to draw in red color. 
</p>


<pre class="explanation">
$cr-&gt;SetSourceRgb(1.0, 1.0, 0.72);
$cr-&gt;Rectangle(0, 0, $full, 30);
$cr-&gt;Clip();
$cr-&gt;Paint();
$cr-&gt;ResetClip();
</pre>

<p>
We draw a yellow rectangle up to point, where the medium is full.
</p>

<pre class="explanation">
$te = $cr-&gt;TextExtents($this-&gt;num[$i-1]);
$cr-&gt;MoveTo($i*$step-$te['width']/2, 15);
$cr-&gt;TextPath($this-&gt;num[$i-1]);
$cr-&gt;Stroke();
</pre>

<p>
This code here draws the numbers on the burning widget. We calculate the
text extents to position the text correctly.
</p>


<pre class="explanation">
public function on_changed($sender) {

    $this-&gt;cur_value = $sender-&gt;get_value();
    $this-&gt;burning-&gt;queue_draw();
}
</pre>

<p>
We get the value from the scale widget, store it in the <code>$this->cur_value</code> variable 
for later use. We redraw the burning widget. 
</p>


<img src="/img/gui/phpgtk/burning.png" alt="Burning widget">
<div class="figure">Figure: Burning widget</div>


<p>
In this chapter, we created a custom widget in GTK and PHP programming language.
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified September 8, 2011  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

